package com.lingdong.onlinejudge.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lingdong.onlinejudge.common.ErrorCode;
import com.lingdong.onlinejudge.exception.BusinessException;
import com.lingdong.onlinejudge.judge.service.JudgeService;
import com.lingdong.onlinejudge.mapper.MatchMapper;
import com.lingdong.onlinejudge.mapper.SignMapper;
import com.lingdong.onlinejudge.mapper.TopicMapper;
import com.lingdong.onlinejudge.model.dto.submit.SubmitDto;
import com.lingdong.onlinejudge.model.dto.submit.SubmitQueryDto;
import com.lingdong.onlinejudge.model.dto.submit.SubmitUpdateExpDto;
import com.lingdong.onlinejudge.model.dto.submit.SumSubmitExpGroupByUserDto;
import com.lingdong.onlinejudge.model.entity.Match;
import com.lingdong.onlinejudge.model.entity.Sign;
import com.lingdong.onlinejudge.model.entity.Submit;
import com.lingdong.onlinejudge.model.entity.Topic;
import com.lingdong.onlinejudge.model.vo.user.UserVo;
import com.lingdong.onlinejudge.service.AuthService;
import com.lingdong.onlinejudge.service.SignService;
import com.lingdong.onlinejudge.service.SubmitService;
import com.lingdong.onlinejudge.mapper.SubmitMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * @author lzw
 */
@Service
public class SubmitServiceImpl extends ServiceImpl<SubmitMapper, Submit>
        implements SubmitService {

    @Resource
    private SubmitMapper submitMapper;

    @Resource
    private AuthService authService;

    @Resource
    private TopicMapper topicMapper;

    @Resource
    private MatchMapper matchMapper;

    @Resource
    private JudgeService judgeService;


    @Resource
    private SignService signService;

    /**
     * 提交代码
     *
     * @param submitDto 提交信息
     * @param token     token
     * @return 结果
     */
    @Override
    public boolean submit(SubmitDto submitDto, String token) {
        String submitCode = submitDto.getSubmitCode();
        Long topicId = submitDto.getTopicId();
        Long matchId = submitDto.getMatchId();
        UserVo userInfo = authService.getUserInfo(token);
        // 判断题目与比赛id是否合法
        if (topicId == null || matchId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 校验比赛id
        QueryWrapper<Match> matchQueryWrapper = new QueryWrapper<>();
        matchQueryWrapper.eq("id", matchId);
        Match match = matchMapper.selectOne(matchQueryWrapper);
        if (match == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }

        // 判断当前登录人有没有报名比赛
        Sign sign = signService.isSign(userInfo.getId(), matchId);

        if (ObjectUtil.isNull(sign)) {
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR);
        }

        // 判断是否在比赛内
        Date startTime = match.getStartTime();
        Date endTime = match.getEndTime();
        Date now = new Date();
        if (now.before(startTime) || now.after(endTime)) {
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR);
        }
        // 校验题目
        QueryWrapper<Topic> topicQueryWrapper = new QueryWrapper<>();
        topicQueryWrapper.eq("id", topicId);
        Topic topic = topicMapper.selectOne(topicQueryWrapper);
        if (topic == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        long submitId = 0;
        // 判断是否已经提交过
        QueryWrapper<Submit> submitQueryWrapper = new QueryWrapper<>();
        submitQueryWrapper.eq("topic_id", topicId)
                .eq("match_id", matchId)
                .eq("create_by", userInfo.getId());
        Submit submits = submitMapper.selectOne(submitQueryWrapper);
        if (submits == null) {
            // 如果为空，证明没有提交过
            Submit submit = new Submit();
            submit.setSubmitCode(submitCode);
            submit.setTopicId(topicId);
            submit.setMatchId(matchId);
            submit.setCreateBy(userInfo.getId());
//            submit.setSubmitExp(topic.getTopicExp());
            boolean save = this.save(submit);
            if (!save) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR);
            }
            submitId = submit.getId();
        } else {
            // 如果不为空，证明提交过 - 更新
            submits.setSubmitCode(submitCode);
            submits.setJudgeTime(null);
            submits.setJudgeInfo(null);
            submits.setSubmitStatus(0);
            boolean update = this.updateById(submits);
            if (!update) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR);
            }
            submitId = submits.getId();
        }
        Integer topicType = topic.getTopicType();
        if (topicType == 0) {
            long finalSubmitId = submitId;
            CompletableFuture.runAsync(() -> {
                judgeService.doJudge(finalSubmitId);
            });
        }
        return true;
    }

    /**
     * 根据比赛id 获取 当前登录角色所有的提交
     *
     * @param matchId 比赛id
     * @param token   用户token
     * @return 提交列表
     */

    @Override
    public List<Submit> queryAllSubmit(long matchId, String token) {
        // 参数校验
        if (ObjectUtil.isNull(matchId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 根据token 解析用户信息
        UserVo userInfo = authService.getUserInfo(token);

        // 查询数据
        return lambdaQuery()
                .eq(Submit::getMatchId, matchId)
                .eq(Submit::getCreateBy, userInfo.getId())
                .list();
    }


    /**
     * 根据提交id 添加分值
     *
     * @param updateDto id 分值
     * @return 是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateSubmitExp(SubmitUpdateExpDto updateDto) {
        // 参数校验
        // 提交id
        Long id = updateDto.getId();
        if (ObjectUtil.isNull(id)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 根据id查找详情提交详情
        Submit submit = submitMapper.selectById(id);
        if (ObjectUtil.isNull(submit)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // 根据提交信息 查找题目信息 (分值)
        Topic topic = topicMapper.selectById(submit.getTopicId());
        // 获取题目的分值
        Integer topicExp = topic.getTopicExp();
        // 获取请求参数的分值
        Integer submitExp = updateDto.getSubmitExp();
        // 请求参数分值不能小于0 且 不能大于题目预设分值
        if (submitExp < 0 || submitExp > topicExp) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        return lambdaUpdate().eq(Submit::getId, id)
                .set(Submit::getSubmitExp, submitExp)
                .set(Submit::getSubmitStatus,2)
                .update();
    }

    /**
     * 根据比赛id 结算分值(按照人来分组)
     *
     * @param matchId 比赛id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateAllUserExpToSign(long matchId) {
        // 结算前校验,提交状态必须都成功
        if (lambdaQuery().eq(Submit::getMatchId,matchId)
                .ne(Submit::getSubmitStatus,2)
                .exists()) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR);
        }


        // 根据比赛查出所有提交,并且根据创建者分组,对分数求和
        List<SumSubmitExpGroupByUserDto> group = submitMapper.selectSumExpGroupByUser(matchId);

        // 遍历修改分数
        group.forEach(item -> {
            signService.lambdaUpdate()
                    .eq(Sign::getUserId, item.getUserId())
                    .eq(Sign::getMatchId, item.getMatchId())
                    .set(Sign::getSignExp, item.signExp)
                    .update();
        });

        return true;
    }

    /**
     * 查找试题提交记录
     * @param queryDto 比赛id 用户id 试题id
     * @return
     */
    @Override
    public Submit selectOneSubmit(SubmitQueryDto queryDto) {
        Long userId = queryDto.getUserId();
        Long topicId = queryDto.getTopicId();
        Long matchId = queryDto.getMatchId();
        if (ObjectUtil.isNull(userId)) {
            throw  new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (ObjectUtil.isNull(topicId)) {
            throw  new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (ObjectUtil.isNull(matchId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        return lambdaQuery().eq(Submit::getCreateBy, userId)
                .eq(Submit::getTopicId, topicId)
                .eq(Submit::getMatchId, matchId)
                .one();
    }

    /**
     * 根据试题id查找提交列表
     *
     * @param topicId 试题id
     * @param matchId 比赛id
     * @return 提交列表
     */
    @Override
    public List<Submit> getSubmitList(Long topicId, Long matchId) {
        if (ObjectUtil.isNull(topicId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        if (ObjectUtil.isNull(matchId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        return submitMapper.selectSubmitList(matchId,topicId);
    }
}




